#define _GNU_SOURCE
#include "app_device.h"
#include "app_common.h"
#include "app_message.h"
#include "app_mqtt.h"
#include "app_pool.h"
#include <stdlib.h>
#include <fcntl.h>  // file control
#include <unistd.h>
#include "log/log.h"

#define BUFFER_SIZE 1024

static Device *device;
/**
 * 初始化设备  => 只需要一台就可以
 */
Device *app_device_init(char *filename)
{
    // 1. 为device申请内存，分配一个Device结构体的内存空间
    device = (Device *)malloc(sizeof(Device));   
    // 2. 设置device的filename属性为传入的filename
    device->filename = filename;   
    // 3. 打开文件并为device的fd属性分配文件描述符
    // 以读写模式打开文件，文件权限为0644
    device->fd = open(filename, O_RDWR, 0644);   
    // 4. 初始化设备的上传缓冲区
    // 调用app_buffer_init函数初始化上传缓冲区，缓冲区大小为BUFFER_SIZE
    device->up_buffer = app_buffer_init(BUFFER_SIZE);   
    // 5. 初始化设备的下载缓冲区
    // 调用app_buffer_init函数初始化下载缓冲区，缓冲区大小为BUFFER_SIZE
    device->down_buffer = app_buffer_init(BUFFER_SIZE);    
    // 6. 设置设备的初始状态为停止（is_running = 0）
    device->is_running = 0;    
    // 7. 设置设备的最后写入时间为0，表示尚未进行任何写入
    device->last_write_time = 0;  
    // 8. 设置设备的回调函数为NULL
    device->post_read = NULL;  // 将蓝牙数据转换为字符数组消息
    device->pre_write = NULL;  // 将字符数组消息转换为蓝牙数据
    // 9. 初始化线程池，设置线程池的线程数为5
    app_pool_init(5);
    // 10. 初始化MQTT客户端模块
    app_mqtt_init();
    // 11. 返回初始化好的设备结构体指针
    return device;
}

/**
 *  发送远程消息的任务函数 => 线程池中的分线程调用
 */
int send_task_func(void *arg)
{
    Device *dev = (Device *)arg;
    // 从上行缓冲区读取出一个字符数组消息数据
    char chars_buf[128];
    //存入缓冲区中
    int len = app_buffer_read(dev->up_buffer, chars_buf, sizeof(chars_buf));
    // 转换为json格式消息
    char *json = app_message_charsToJson(chars_buf, len);
    // 发送消息到远程
    int result = app_mqtt_send(json);
    if (result == -1)
    {
        log_error("发送消息到远程失败");
        return -1;
    }
    log_debug("发送消息到远程成功: %s", json);
    return 0;
}
//线程函数 => 读取串口文件中的数据-》》将数据存储到设备的上传缓冲区中-》》将发送消息到远程交给线程池去处理-》通过mqtt发送
static void* read_thread_func(void *arg)
{
    // 不断读取串口文件中的数据
    while (device->is_running)
    {
        char data_buf[128];
        ssize_t data_len = read(device->fd, data_buf, sizeof(data_buf));
        
        // 读取串口文件中的数据，并存储到设备的上传缓冲区中
        if (data_len > 0)
        {
        app_buffer_write(device->up_buffer, data_buf, data_len);
        //将发送消息到远程交给线程池去处理
        app_pool_registerTask(send_task_func, device);
        }
    }
}
//________________________________-_______________________________
/**
 * 从下行缓冲区读取数据写到串口文件的任务函数 => 线程池中的分线程调用
 */
int write_task_func(void *arg)
{
    char chars_buf[128];
    int len = app_buffer_read(device->down_buffer, chars_buf, sizeof(chars_buf));
    if (len == -1)
    {
        log_error("从下行缓冲区读取数据失败");
        return -1;
    }
    // 如果与上一次写的时间小于200ms, 那等待相关时间
    long distance_time = app_common_getCurrentTime() - device->last_write_time;
    if (distance_time < 200)
    {
        usleep((200 - distance_time) * 1000);
    }
    //写到串口文件中
    ssize_t write_len = write(device->fd, chars_buf, len);
    if (write_len == -1)
    {
        
        log_error("写入串口文件失败");
        return -1;
    }
    log_debug("写入串口文件成功: %.*s", len , chars_buf);
        // 更新最后一次写时间
    device->last_write_time = app_common_getCurrentTime();
}
/**
 * 接收远程消息的回调函数(必须要接受json格式消息)
 */
static int receive_msg_callback(char *json)
{
    // 将json消息转换为字符数组消息
    char chars_buf[128];
    int len = app_message_jsonToChars(json, chars_buf, sizeof(chars_buf));
    log_debug("接收远程消息: %.*s", len, chars_buf);
    // 将字符数组保存到下行缓冲区
    app_buffer_write(device->down_buffer, chars_buf, len);
    // 将下行缓冲区的数据写入串口文件（交给线程池去处理）
    app_pool_registerTask(write_task_func, device);
    return 0;
}
/**
 * 启动设备
 */
int app_device_start()
{
    if (device->is_running == 1)
    {
        return -1;
    }
    device->is_running = 1;
    //启动读线程 添加线程函数
    /*每个设备（Device）可能会有独立的线程来读取串口数据或进行其他任务。
    如果将线程标识（pthread_t）存储在 Device 结构体中，就能确保每个设备实例与其对应的线程绑定
    */
    pthread_create(&device->read_thread, NULL, read_thread_func, NULL);
    //注册接受远程消息的回调函数
    app_mqtt_registerRecvCallback(receive_msg_callback);
    return 0;
}

/**
 * 销毁设备
 */
void app_device_destroy()
{
    //关闭文件
    close(device->fd);
    //关闭缓存区
    app_buffer_free(device->up_buffer);
    app_buffer_free(device->down_buffer);
    //关闭线程
    /*取消线程：首先通过 pthread_cancel 请求取消线程 device->read_thread。该线程可能会在某个合适的位置响应取消请求并终止。
     等待线程终止：然后使用 pthread_join 等待线程 device->read_thread 结束，确保线程完全退出后，主线程才会继续执行。
     pthread_join 会阻塞当前线程，直到目标线程（即 device->read_thread）终止*/
    pthread_cancel(device->read_thread);
    pthread_join(device->read_thread, NULL);
    //关闭device
    free(device);
    //关闭线程池
    app_pool_destroy();
    //关闭mqtt
    app_mqtt_close();
}